package marlon.smsbatcheditor.db;

import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import marlon.smsbatcheditor.Constants;
import marlon.smsbatcheditor.model.Rule;
import marlon.smsbatcheditor.model.RuleAction;
import marlon.smsbatcheditor.model.RuleCondition;
import marlon.smsbatcheditor.model.Sms;
import marlon.smsbatcheditor.model.RuleCondition.Contains;
import marlon.smsbatcheditor.model.RuleCondition.Read;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;

/**
 * 
 * @author marlonyao<yaolei135@gmail.com>
 */
public class SmsDAO {
//	private Context context;
	private Uri smsUri;
	private ContentResolver contentResolver;
	
	public SmsDAO(Context context) {
//		this.context = context;
		this.smsUri = Uri.parse("content://sms");
		this.contentResolver = context.getContentResolver();
	}
	
	public Cursor find(Rule rule) {
		return contentResolver.query(smsUri, null, constructSelection(rule), null, "date desc");
	}

	public Sms findById(long smsId) {
//		Uri uri = Uri.withAppendedPath(smsUri, String.valueOf(smsId));
		Cursor cursor = contentResolver.query(smsUri, null, "_id="+smsId, null, null);
		if (cursor.moveToFirst()) {
			Log.d(Constants.TAG, "return a real sms");
			return new Sms(cursor);
		}
		Log.d(Constants.TAG, "return null sms");
		return null;
	}
	
	private String constructSelection(Rule rule) {
		StringBuilder sb = new StringBuilder();
		
		// TODO: escape special character like ', "
		for (Iterator<RuleCondition> itor = rule.getConditions(); itor.hasNext();) {
			RuleCondition cond = itor.next();
			if (cond.getTerm() == RuleCondition.Term.ALL) {
				RuleCondition.Read read = (Read) cond.getPredict();
				if (read == RuleCondition.Read.READ) {
					sb.append("read=1");
				} else if (read == RuleCondition.Read.UNREAD) {
					sb.append("read=0");
				} else {	// all
					sb.append("1=1");
				}
			} else {
				String term = null;
				if (cond.getTerm() == RuleCondition.Term.ADDRESS) {
					term = "address";
				} else if (cond.getTerm() == RuleCondition.Term.BODY) {
					term = "body";
				}
				sb.append(term).append(" ");
				
				RuleCondition.Contains contains = (Contains) cond.getPredict();
				if (contains == RuleCondition.Contains.EQUALS) {
					sb.append("like " + quoteString(cond.getValue()));
				} else if (contains == RuleCondition.Contains.STARTS_WITH) {
					sb.append("like " + quoteString(cond.getValue() + "%"));
				} else if (contains == RuleCondition.Contains.ENDS_WITH) {
					sb.append("like " + quoteString("%" + cond.getValue()));
				} else if (contains == RuleCondition.Contains.CONTAINS) {
					sb.append("like " + quoteString("%" + cond.getValue() + "%"));
				}
			}
			if (itor.hasNext()) {
				sb.append(" and ");
			}
		}
		
		Log.d(Constants.TAG, "query cond: " + sb);
		return sb.toString();
	}
	
	private static String quoteString(String str) {
		return "'" + str.replace("'", "''") + "'";	// TODO: consider special character 
	}

	public boolean update(Sms sms, Rule rule) {
		Uri uri = Uri.withAppendedPath(smsUri, String.valueOf(sms.getId()));
		
		boolean isRemove = false;
		ContentValues values = new ContentValues();
		for (Iterator<RuleAction> itor = rule.getActions(); itor.hasNext();) {
			RuleAction action = itor.next();
			if (action.getType() == RuleAction.Type.REMOVE) {
				isRemove = true;
				break;
			} else if (action.getType() == RuleAction.Type.MARK_AS_READ) {
				values.put("read", 1);
			} else if (action.getType() == RuleAction.Type.MARK_AS_UNREAD) {
				values.put("read", 0);
			} else {
				String value = parseValue(sms, action.getValue());
				if (action.getType() == RuleAction.Type.MODIFY_ADDRESS) {
					values.put("address", value);
				} else if (action.getType() == RuleAction.Type.MODIFY_BODY) {
					values.put("body", value);
				}
			}
		}
		if (isRemove) {
			return contentResolver.delete(uri, null, null) > 0;
		} else {
			return contentResolver.update(uri, values, null, null) > 0;
		}
	}

	private static final Pattern VAR_PATTERN = Pattern.compile("\\$(BODY|ADDRESS)\\$");
	
	// replace $BODY$, $ADDRESS$
	private String parseValue(Sms sms, String value) {
		Matcher m = VAR_PATTERN.matcher(value);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			String var = m.group(1);
			if ("BODY".equals(var)) {
				m.appendReplacement(sb, sms.getBody());
			} else if ("ADDRESS".equals(var)) {
				m.appendReplacement(sb, sms.getAddress());
			}
		}
		m.appendTail(sb);
		
		Log.d(Constants.TAG, sb.toString());
		return sb.toString();
	}
}
